import morgan from 'morgan';
import path from 'path';
import { fileURLToPath } from 'url';
import helmet from 'helmet';
import express, { Request, Response, NextFunction, ErrorRequestHandler, RequestHandler } from 'express';
import session from 'express-session';
import mysqlSession from 'express-mysql-session';
import flash from 'connect-flash';
import dayjs from 'dayjs';

import passport from '@src/util/passport';
import apiRouter from '@src/routes';
import viewRouter from '@src/routes/view'
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
import { RouteError } from '@src/util/route-errors';
import { NodeEnvs } from '@src/constants/env';
import { pool } from '@src/db';

import debug from "debug";


const log = debug('app:user-controller');


// ES 模块中获取 __dirname 的替代方案
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const app = express();
const sessionStore = mysqlSession(session);

const NODE_ENV = process.env.NODE_ENV || NodeEnvs.Dev;

/* ------------------------------- Middleware ------------------------------- */

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(session({
  secret: 'secret',
  resave: false,
  saveUninitialized: false,
  store: new sessionStore({}, pool as any),
  cookie: {
    secure: NODE_ENV === NodeEnvs.Production,
  },
}));

app.use(passport.initialize());
app.use(passport.session());

app.use(flash());

// 全局中间件：自动将 flash 消息传递给模板
const flashMiddleware: RequestHandler = (req, res, next): void => {
  // 获取所有类型的 flash 消息
  const successMessages = req.flash('success');
  const errorMessages = req.flash('error');
  const infoMessages = req.flash('info');
  const warningMessages = req.flash('warning');

  // 优先级：error > warning > success > info
  // 只显示第一条消息（可以根据需求修改）
  if (errorMessages.length > 0) {
    res.locals.msg = errorMessages[0];
    res.locals.msgType = 'error';
  } else if (warningMessages.length > 0) {
    res.locals.msg = warningMessages[0];
    res.locals.msgType = 'warning';
  } else if (successMessages.length > 0) {
    res.locals.msg = successMessages[0];
    res.locals.msgType = 'success';
  } else if (infoMessages.length > 0) {
    res.locals.msg = infoMessages[0];
    res.locals.msgType = 'info';
  }

  next();
};

app.use(flashMiddleware);

// 测试环境启用 morgan
if (NODE_ENV !== NodeEnvs.Production) {
  app.use(morgan('dev'));
}

// 生产环境启用 helmet
if (NODE_ENV === NodeEnvs.Production) {
  // eslint-disable-next-line n/no-process-env
  if (!process.env.DISABLE_HELMET) {
    app.use(helmet());
  }
}


/* ---------------------------------- 前端资源 ---------------------------------- */

const viewsDir = path.join(__dirname, 'views');
app.set('views', viewsDir);
app.set('view engine', 'pug');

const staticDir = path.join(__dirname, 'public');
console.log('staticDir', staticDir);
app.use(express.static(staticDir));



/* ---------------------------------- 路由挂载 ---------------------------------- */

const BASE_API = process.env.BASE_API || '/api';
// 路由 视图挂载和API 挂载
app.use('/', viewRouter);
app.use(BASE_API, apiRouter);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  console.log('404', req.url);
  const err = new RouteError(HttpStatusCodes.NOT_FOUND, 'Not Found');
  next(err);
});


// Add error handler
app.use(((err: Error, req: Request, res: Response, next: NextFunction) => {

  // 非测试环境下打印错误日志
  if (NODE_ENV !== NodeEnvs.Production) {
    console.error('❌ Error:', err.message);
    console.error(err.stack);
  }

  // 设置错误状态码和响应
  let status = HttpStatusCodes.BAD_REQUEST;
  let message = 'An error occurred';

  if (err instanceof RouteError) {
    status = err.status;
    message = err.message;
  } else if (NODE_ENV !== NodeEnvs.Production) {
    // 开发环境返回详细错误信息
    message = err.message;
  }

  // 发送错误响应
  return res.status(status).json({
    error: message,
    ...(NODE_ENV !== NodeEnvs.Production && { stack: err.stack })
  });
}) as ErrorRequestHandler);


/* --------------------------------- 挂载模板对象 --------------------------------- */

app.locals.dayjs = dayjs;
app.locals.env = app.get('env');
app.locals.truncateText = function (text: string, length: number) {
  var truncatedText = text.substring(0, length);
  return truncatedText;
};

export default app;
